package crypto

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"errors"
	"fmt"
)

type aesKeyGenerator struct {
	length int
}

//KeyGen 密钥对生成
func (kg *aesKeyGenerator) KeyGen() (Key, error) {
	lowLevelKey, err := RandomBytes(kg.length)
	if err != nil {
		return nil, fmt.Errorf("Failed generating AES %d key [%s]", kg.length, err)
	}

	return &aesPrivateKey{lowLevelKey, true}, nil
}

type aesPrivateKey struct {
	privKey    []byte
	exportable bool
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *aesPrivateKey) Bytes() (raw []byte) {
	if k.exportable {
		return k.privKey
	}

	return nil
}

// SKI returns the subject key identifier of this key.
func (k *aesPrivateKey) SKI() (ski []byte) {
	hash := sha256.New()
	hash.Write([]byte{0x01})
	hash.Write(k.privKey)
	return hash.Sum(nil)
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *aesPrivateKey) IsSymmetric() bool {
	return true
}

// IsPrivate returns true if this key is a private key,
// false otherwise.
func (k *aesPrivateKey) IsPrivate() bool {
	return true
}

//Encrypt encrypt
func (k *aesPrivateKey) Encrypt(data []byte) ([]byte, error) {
	tmp := options.Padding.Padding(data)
	if len(tmp)%aes.BlockSize != 0 {
		return nil, errors.New("Invalid plaintext. It must be a multiple of the block size")
	}
	block, err := aes.NewCipher(k.privKey)
	if err != nil {
		return nil, err
	}

	ciphertext := make([]byte, len(tmp))
	//iv := ciphertext[:aes.BlockSize]
	//if _, err := io.ReadFull(rand.Reader, iv); err != nil {
	//	return nil, err
	//}
	hash := sha256.Sum256(k.privKey)
	iv := hash[:aes.BlockSize]

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext, tmp)

	return ciphertext, nil
}

//Decrypt decrypt
func (k *aesPrivateKey) Decrypt(src []byte) ([]byte, error) {
	block, err := aes.NewCipher(k.privKey)
	if err != nil {
		return nil, err
	}

	if len(src) < aes.BlockSize {
		return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size")
	}
	//iv := src[:aes.BlockSize]
	//src = src[aes.BlockSize:]
	//fmt.Println(hex.EncodeToString(k.privKey))
	hash := sha256.Sum256(k.privKey)
	iv := hash[:aes.BlockSize]
	//fmt.Println(hex.EncodeToString(iv))
	if len(src)%aes.BlockSize != 0 {
		return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, iv)

	mode.CryptBlocks(src, src)
	return options.Padding.UnPadding(src)

}
